#include "resourcemanager.h"

#include <stdlib.h>

#include "luainterface.h"

ResourceManager* resourceManager;

void ResourceManager::LoadAllData(){
//    LoadAllOrders();
    LoadAllBuildings();
    LoadAllUnits();
    LoadAllTerrain();

    LoadModel("simplehouse", "data\\");
    LoadModel("army", "data\\");
    LoadModel("selection_indicator", "data\\");
    LoadModel("underconstruction", "data\\buildings\\models\\");
    LoadModel("construction_symbol", "data\\");
    LoadModel("pause_symbol", "data\\");
    LoadModel("territory_edge", "data\\");

    LoadTexture("button", "data\\button.tga");
    LoadTexture("resume", "data\\resume.tga");
    LoadTexture("transparent50", "data\\transparent50.tga");
    LoadTexture("river", "data\\river.tga");
    LoadTexture("road", "data\\road.tga");
}

GLuint ResourceManager::LoadTexture(std::string name, std::string filename){
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glfwLoadTexture2D(filename.c_str(), GLFW_BUILD_MIPMAPS_BIT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);
    textures.insert(std::pair<std::string,GLuint>(name, texture));
    return texture;
}

// BULIDINGS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ResourceManager::LoadAllBuildings(){
    std::cout<<"Loading Buildings:\n";
    std::vector<std::string> files;
    std::string file;
    struct dirent *ep;
    DIR *dir = opendir("data\\buildings");

    if(dir!=nullptr){
        while((ep = readdir(dir))){
            file = ep->d_name;
            if(file.find(".lua")!=std::string::npos){
                files.push_back(file);
            }
        }
        closedir(dir);
    }
    for(unsigned int i=0;i<files.size();i++){
        LoadBuilding(files.at(i));
    }
}
void ResourceManager::LoadBuilding(std::string filename){
    std::cout<<" - "<<std::string("data\\buildings\\")+filename<<std::endl;

    std::unique_ptr<Building> building(new Building());
    std::string prereq;
    std::set<std::string> constructionOptionNames;
    std::set<std::string> recruitmentOptionNames;
    std::string replaces;
    GLuint texture;

    if(lua.LoadValues(std::string("data\\buildings\\")+filename) == false){
        std::cout<<"ResourceManager::LoadBuilding Error: couldn't load values for "<<filename<<std::endl;
        return;
    }

    //General variables
    building->SetName(lua.GetString("name"));
    building->SetCanBeBuiltInsideCity(lua.GetInt("buildInsideCity"));
    building->SetCanBeBuiltOutsideCity(lua.GetInt("buildOutsideCity"));
    building->SetTurnsToComplete(lua.GetInt( "buildtime"));
    building->SetCanContainUnits(lua.GetInt("cangarrison"));
    building->SetFarmingDistance(lua.GetFloat("farming_distance"));
    building->SetAdministrativeDistance(lua.GetFloat("administrative_distance"));
    building->SetPopulationLimit(lua.GetInt("population_limit"));

    texture = LoadTexture(building->GetName(), std::string("data\\buildings\\buildpics\\")+lua.GetString("buildpic"));
    building->SetBuildPic(texture);

    building->SetPrerequisite(lua.GetString("prereq"));

    building->SetResearchPrerequisites(lua.GetStringList("researchprereqs"));
    building->SetReplaces(lua.GetString("replaces"));
    recruitmentOptionNames = lua.GetStringSet("recruitoptions");
    if(recruitmentOptionNames.size()>0){
        building->SetRecruitmentOptionNames(recruitmentOptionNames);
    }
    constructionOptionNames = lua.GetStringSet("constructionoptions");
    if(constructionOptionNames.size()>0){
        building->SetConstructionOptionNames(constructionOptionNames);
    }
    building->SetModelName(lua.GetString("model"));
    LoadModel(building->GetModelName(), std::string("data\\buildings\\models\\"));

    buildings.insert(std::pair<std::string,std::unique_ptr<Building>>(building->GetName(),std::move(building)));
}

void ResourceManager::LoadModel(std::string name, std::string path){
    if(models.find(name)==models.end()){
        std::unique_ptr<Model> model(new Model(path+name));
        if(model->GetNumTriangles()!=0){
            models.insert(std::pair<std::string,std::unique_ptr<Model>>(name,std::move(model)));
        }
    }
}

// UNITS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ResourceManager::LoadAllUnits(){
    std::cout<<"Loading Units:\n";
    std::vector<std::string> files;
    std::string file;
    struct dirent *ep;
    DIR *dir = opendir("data\\units");

    if(dir!=nullptr){
        while((ep = readdir(dir))){
            file = ep->d_name;
            if(file.find(".lua")!=std::string::npos){
                files.push_back(file);
            }
        }
        closedir(dir);
    }
    for(unsigned int i=0;i<files.size();i++){
        LoadUnit(files.at(i));
    }
}
void ResourceManager::LoadUnit(std::string filename){
    GLuint texture;

    std::cout<<" - "<<std::string("data\\units\\")+filename<<std::endl;
    std::unique_ptr<Unit> unit(new Unit());

    if(lua.LoadValues(std::string("data\\units\\")+filename) == false){
        return;
    }
    unit->SetName(lua.GetString("name"));
    unit->SetSoldiers(lua.GetInt("soldiers"));
    unit->SetTurnsToComplete(lua.GetInt("productioncost"));

    std::vector<std::string> orderList = lua.GetStringList("orders");
    for(unsigned int i=0;i<orderList.size();i++){
        unit->AddOrder(orderList.at(i));
    }
    texture = LoadTexture(unit->GetName(), std::string("data\\units\\buildpics\\")+lua.GetString("buildpic"));
    unit->SetBuildPic(texture);

    units.insert(std::pair<std::string,std::unique_ptr<Unit>>(unit->GetName(),std::move(unit)));
}

// TERRAIN /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ResourceManager::LoadAllTerrain(){
    std::cout<<"Loading Terrain:\n";
    std::vector<std::string> files;
    std::string file;
    struct dirent *ep;
    DIR *dir = opendir("data\\terrain");

    if(dir!=nullptr){
        while((ep = readdir(dir))){
            file = ep->d_name;
            if(file.find(".lua")!=std::string::npos){
                files.push_back(file);
            }
        }
        closedir(dir);
    }
    for(unsigned int i=0;i<files.size();i++){
        LoadTerrain(files.at(i));
    }
}
void ResourceManager::LoadTerrain(std::string filename){
    std::cout<<" - "<<std::string("data\\terrain\\")+filename<<std::endl;
    if(lua.LoadValues(std::string("data\\terrain\\")+filename)==false){
        return;
    }
    std::string name = lua.GetString("name");
    if(lua.GetInt("typeof")==1){
        std::unique_ptr<TerrainType> terrain(new TerrainType(name, lua.GetFloat("food"), lua.GetInt("coin"), lua.GetInt("production"), lua.GetFloat("movementmoneycost"), lua.GetString("texture")));
        terrainTypes.insert(std::pair<std::string,std::unique_ptr<TerrainType>>(name,std::move(terrain)));
    }
    else if(lua.GetInt("typeof")==2){
        std::unique_ptr<TerrainFeature> feature(new TerrainFeature(name, lua.GetFloat("food"), lua.GetInt("coin"), lua.GetInt("production"), lua.GetFloat("movementmoneycost")));
        feature->SetHeightmap(lua.GetFloatList("heightmap"));
        terrainFeatures.insert(std::pair<std::string,std::unique_ptr<TerrainFeature>>(name,std::move(feature)));
    }
    else{
        std::cout<<"Cannot determine if terrain type or feature: "<<name<<std::endl;
    }
}
// Getters /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Entity* ResourceManager::GetEntity(std::string name){
    if(Building* building = GetBuilding(name)){
        return building;
    }
    else if(GetUnit(name)!=nullptr){
        return GetUnit(name);
    }
    else return nullptr;
}
Building* ResourceManager::GetBuilding(std::string name){
    if(buildings.find(name)!=buildings.end()){
        return buildings.find(name)->second.get();
    }
    else{
        return nullptr;
    }
}
Unit* ResourceManager::GetUnit(std::string name){
    if(units.find(name)!=units.end()){
        return units.find(name)->second.get();
    }
    else{
        return nullptr;
    }
}
TerrainType* ResourceManager::GetTerrainType(std::string name){
    if(terrainTypes.find(name)!=terrainTypes.end()){
        return terrainTypes.find(name)->second.get();
    }
    return terrainTypes.find("grassland")->second.get();
}
std::vector<TerrainType*> ResourceManager::GetAllTerrainTypes(){
    std::vector<TerrainType*> terrains;
    for(const auto & terrain : terrainTypes){
        terrains.push_back(terrain.second.get());
    }
    return terrains;
}
TerrainFeature* ResourceManager::GetTerrainFeature(std::string name){
    if(terrainFeatures.find(name)!=terrainFeatures.end()){
        return terrainFeatures.find(name)->second.get();
    }
    return terrainFeatures.find("flat")->second.get();
}
Model* ResourceManager::GetModel(std::string name){
    if(models.find(name)!=models.end()){
        return models.find(name)->second.get();
    }
    else{
//        std::cout<<"Model not found: "<<name<<std::endl;
        return nullptr;
    }
}
GLuint ResourceManager::GetTexture(std::string name){
    if(textures.find(name)!=textures.end()){
        return textures.find(name)->second;
    }
    else{
        std::cout<<"Texture not found: "<<name<<std::endl;
        return -1;
    }
}
